package com.utils.gson;

import com.google.gson.*;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.utils.gson.adapter.LocalDateAdapter;
import com.utils.gson.adapter.LocalDateTimeAdapter;
import com.utils.gson.adapter.LocalTimeAdapter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;

/**
 * Gson工具类 2020-03-26<br/>
 * 1,把json转换成Object<br/>
 * 2,把Object转换成json<br/>
 * 3,该方法主要功能是将json字符串转换成指定类型的对象<br/>
 */
public class GsonUtils {

    /**
     * Gson对象
     */
    private static Gson gson = null;

    /**
     * JsonParser对象
     */
    private static JsonParser jsonParser = null;


    /**
     * 自定义TypeAdapter ,null对象将被解析成空字符串
     */
    static final TypeAdapter<String> STRING = new TypeAdapter<String>() {

        @Override
        public String read(JsonReader reader) {
            try {
                if (reader.peek() == JsonToken.NULL) {
                    reader.nextNull();
                    return "";//原先是返回Null，这里改为返回空字符串
                }
                return reader.nextString();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "";
        }

        @Override
        public void write(JsonWriter writer, String value) {
            try {
                if (value == null) {
                    writer.nullValue();
                    return;
                }
                writer.value(value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    /**
     * 自定义adapter，解决由于数据类型为Int,实际传过来的值为Float，导致解析出错的问题
     * 目前的解决方案为将所有Int类型当成Double解析，再强制转换为Int
     */
    static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
        @Override
        public Number read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return 0;
            }
            try {
                double i = in.nextDouble();
                return (int) i;
            } catch (NumberFormatException e) {
                throw new JsonSyntaxException(e);
            }
        }

        @Override
        public void write(JsonWriter out, Number value) throws IOException {
            out.value(value);
        }
    };

    static {
        GsonBuilder gsonBulder = new GsonBuilder();
        gsonBulder.registerTypeAdapter(String.class, STRING);   //所有String类型null替换为字符串“”
        gsonBulder.registerTypeAdapter(int.class, INTEGER); //int类型对float做兼容
        //通过反射获取instanceCreators属性
        try {
            Class builder = (Class) gsonBulder.getClass();
            Field f = builder.getDeclaredField("instanceCreators");
            f.setAccessible(true);
            Map<Type, InstanceCreator<?>> val = (Map<Type, InstanceCreator<?>>) f.get(gsonBulder);//得到此属性的值
            //注册数组的处理器
            gsonBulder.registerTypeAdapterFactory(new CollectionTypeAdapterFactory(new ConstructorConstructor(val)));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        gsonBulder.setDateFormat("yyyy-MM-dd HH:mm:ss");
        gsonBulder.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter());
        gsonBulder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
        gsonBulder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());

        gsonBulder.setPrettyPrinting()
                .serializeNulls();   //在序列化的时候不忽略null值
        gson = gsonBulder.create();
        jsonParser = new JsonParser();
    }


    /**
     * 1，该方法主要功能是将json字符串转换成JsonObject
     *
     * @param json json字符串
     * @return 解析后的JsonObject
     * @throws JsonSyntaxException 如果解析中出了问题，或者是json不完整都会抛出这个异常信息
     */
    public static JsonObject parseJson(String json) throws JsonSyntaxException {
        return gson.fromJson(json, JsonObject.class);
    }

    /**
     * 2，该方法主要功能是将json字符串转换成JsonArray
     *
     * @param json json字符串
     * @return 解析后的JsonArray
     * @throws JsonSyntaxException 如果解析中出了问题，或者是json不完整都会抛出这个异常信息
     */
    public static JsonArray parseJsonArray(String json) throws JsonSyntaxException {
        return jsonParser.parse(json).getAsJsonArray();
    }

    /**
     * 3，该方法主要功能是将json字符串转换成java对象
     *
     * @param json json字符串
     * @return 解析后的java对象
     * @throws JsonSyntaxException 如果解析中出了问题，或者是json不完整都会抛出这个异常信息
     */
    public static <T> T fromJson(String json, Class<T> clas) throws JsonSyntaxException {
        return gson.fromJson(json, clas);
    }

    /**
     * 4，该方法主要功能是将Java类对象转换成json字符串
     *
     * @param obj Java对象
     * @return json字符串
     */
    public static String toJson(Object obj) {
        return gson.toJson(obj);
    }

}
